/*
 * Virtual Earth Screensaver
 * Keith Kinnan
 * 5/12/2007
 * 9/30/2006
 * 
 * Portions of this code is based on the Visual Studio 2005 Starter Kit RSS Screen Saver:
 * http://msdn.microsoft.com/vstudio/express/visualcsharp/starterkit/default.aspx#rssss
 */

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using System.Text;
using System.Reflection;
using System.Globalization;
using System.ComponentModel;

namespace VirtualEarthScreensaver
{
    /// <summary>
    /// Screen Saver Form
    /// </summary>
    partial class ScreenSaverForm : Form
    {
        private Program.ExitCode m_returnCode = Program.ExitCode.Exit;
        private BackgroundWorker backgroundWorkerThread;
        private List<WebBrowser> webBrowsers = new List<WebBrowser>();
        private List<Form> emptyForms = new List<Form>();

        /// <summary>
        /// Return Code
        /// </summary>
        public Program.ExitCode ReturnCode 
        {
            get{ return m_returnCode;}
            set{ m_returnCode = value; }
        }

        // The RssFeeds to display scenes from
        private List<RssFeed> rssFeedList = new List<RssFeed>();
        
        private string m_htmlContent = string.Empty;

        // Keep track of whether the screensaver has become active.
        private bool isActive;

        // Keep track of the location of the mouse
        private Point mouseLocation;

        public ScreenSaverForm()
        {
            InitializeComponent();
            SetupScreenSaver();
        }


        /// <summary>
        /// Set up the screensaver
        /// </summary>
        private void SetupScreenSaver()
        {

            // Use double buffering to improve drawing performance
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
            // Capture the mouse
            this.Capture = true;

            // Set the application to full screen mode and hide the mouse
            Cursor.Hide();

            //Get the total size of all monitors and set it for the control
            this.Bounds = GetAllScreenBounds();
            this.ShowInTaskbar = false;
            this.DoubleBuffered = true;
            this.BackgroundImageLayout = ImageLayout.Stretch;

            //Read the HTML content
            m_htmlContent = ReadEmbeddedResource("VirtualEarthScreensaver.Resources.vescreensaver.htm");
            m_htmlContent = m_htmlContent.Replace("##VIRTUAL_EARTH_API_URL##", Properties.Settings.Default.VirtualEarthAPIUrl);
            m_htmlContent = m_htmlContent.Replace("##UPDATE_INTERVAL##", System.Convert.ToInt32(Properties.Settings.Default.UpdateTime * 1000).ToString(CultureInfo.InvariantCulture));
            m_htmlContent = m_htmlContent.Replace("##PRESS_ENTER##", Properties.Resources.StrPressEnter);

            //Set the Feed update timer interval
            feedUpdateTimer.Interval = System.Convert.ToInt32(Properties.Settings.Default.FeedRefreshTime) * 60000;

            //If there is 1 screen, or multiple screens with Stretch Scene option on
            if ((Screen.AllScreens.Length == 1) || (Properties.Settings.Default.StretchScene))
            {
                //Stretch the web browser across all screens
                CreateWebBrowser(this.Bounds);
            }
            //if the option is Show Multiple Scenes and there are multiple screens
            else if (Properties.Settings.Default.ShowMultipleScenes)
            {
                //Create a web browser for each screen
                foreach (Screen screen in Screen.AllScreens)
                {
                    CreateWebBrowser(screen.Bounds);
                }
            }
            //if there are multiple screens and the option is to show only one scene
            else
            {
                foreach (Screen screen in Screen.AllScreens)
                {
                    //Create the web browser on the Primary screen
                    if (screen.Primary)
                    {
                        CreateWebBrowser(screen.Bounds);
                    }
                    //Show a black screen on the other screens
                    else
                    {
                        EmptyForm emptyForm = new EmptyForm();
                        emptyForm.Bounds = screen.Bounds;
                        emptyForm.Show(this);
                        emptyForms.Add(emptyForm);
                    }
                }
            }


            // for all the web browsers instantiated, show Loading scene
            foreach (WebBrowser webBrowser in webBrowsers)
            {
                //Show Loading screen
                webBrowser.DocumentText = m_htmlContent.Replace("##SCENES##", string.Format(CultureInfo.InvariantCulture, "a.push(['{0}','{1}','{2}']);", Properties.Resources.StrDefaultLiveSearchMapsUrl, Properties.Resources.StrLoadingRssFeeds, Properties.Settings.Default.RssFeedUri.Replace(";", "<br/>")));
            }

            //Load the RSS feeds and scene data in a background worker thread
            backgroundWorkerThread = new BackgroundWorker();
            backgroundWorkerThread.DoWork += new DoWorkEventHandler(backgroundWorkerDoWork);
            backgroundWorkerThread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorkerCompleted);
            backgroundWorkerThread.RunWorkerAsync();
        }

        /// <summary>
        /// Create WebBrowser
        /// Helper function to create and show WebBrowser controls
        /// </summary>
        /// <param name="bounds">Bounds for the web browser</param>
        private void CreateWebBrowser(Rectangle bounds)
        {
            WebBrowser webBrowser = new WebBrowser();
            webBrowser.Dock = DockStyle.None;
            webBrowser.ScrollBarsEnabled = false;
            webBrowser.Bounds = bounds;
            webBrowser.Width = bounds.Width;
            webBrowser.Height = bounds.Height;

            //Add it to the control
            this.Controls.Add(webBrowser);

            //store it in the web browser list
            webBrowsers.Add(webBrowser);
        }

        /// <summary>
        /// Get All Screen Bounds
        /// Returns the Rectangle encompassing all the screen bounds for multiple monitors
        /// </summary>
        /// <returns>Rectangle</returns>
        private Rectangle GetAllScreenBounds()
        {
            Rectangle final_rect = Screen.AllScreens[0].Bounds;
            for (int i = 0; i < Screen.AllScreens.Length; i++)
            {
                Rectangle bds = Screen.AllScreens[i].Bounds;
                if (bds.X < final_rect.X)
                    final_rect.X = bds.X;
                if (bds.Y < final_rect.Y)
                    final_rect.Y = bds.Y;

                int rightmost_coordinate = final_rect.Width + final_rect.X;
                int bds_rightmost = bds.Width + bds.X;
                if (bds_rightmost > rightmost_coordinate)
                {
                    final_rect.Width += bds_rightmost - rightmost_coordinate;
                }

                int bottom_most_coordinate = final_rect.Height + final_rect.Y;
                int bds_bottommost = bds.Height + bds.Y;
                if (bds_bottommost > bottom_most_coordinate)
                {
                    final_rect.Height += bds_bottommost - bottom_most_coordinate;
                }

                // We want largest right hand side
            }
            return final_rect;
        }

        /// <summary>
        /// worker thread for background worker thread
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            LoadRssFeeds();
            e.Result = ReadSceneData();
        }

        /// <summary>
        /// completion event for background worker thread
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if ((e.Error == null) && (!e.Cancelled))
            {
                string sceneData = e.Result.ToString();
                foreach (WebBrowser webBrowser in webBrowsers)
                {
                    if (webBrowser != null)
                    {
                        webBrowser.DocumentText = m_htmlContent.Replace("##SCENES##", sceneData);
                    }
                }
            }
        }

        /// <summary>
        /// Load RSS Feeds
        /// </summary>
        private void LoadRssFeeds()
        {
            try
            {
                rssFeedList.Clear();
                char[] delimiter = {';'};
                string[] rssFeedUrls = Properties.Settings.Default.RssFeedUri.Split(delimiter);
                foreach (string rssFeedUrl in rssFeedUrls)
                {
                    if (rssFeedUrl.Trim().Length > 0)
                    {
                        rssFeedList.Add(RssFeed.FromUri(rssFeedUrl));
                    }
                }
            }
            catch (System.Xml.XmlException)
            {
            }
            catch (System.UriFormatException)
            {
            }
            catch (System.Net.WebException)
            {
            }
        }

        /// <summary>
        /// ReadSceneData
        /// </summary>
        /// <returns></returns>
        private string ReadSceneData()
        {
           string scenes = string.Empty;
           StringBuilder sb = new StringBuilder();

            foreach (RssFeed rssFeed in rssFeedList)
            {
                if ((rssFeed != null) && (rssFeed.Channels.Count > 0))
                {
                    if (rssFeed.Channels[0].Items.Count > 0)
                    {
                        for (int i = 0; i < rssFeed.Channels[0].Items.Count; ++i)
                        {
                            RssItem rssItem = VERssReader.Transform(rssFeed.Channels[0].Items[i]);
                            if ((rssItem != null) &&(rssItem.Link != null) && (rssItem.Link.Length > 0))
                            {
                                sb.AppendFormat("a.push([\"{0}\",\"{1}\",\"{2}\"]);", rssItem.Link, rssItem.Title, rssItem.Description);
                            }
                        }
                    }
                }
            }

            scenes = sb.ToString();
            if (scenes.Length == 0)
            {
                scenes = string.Format(CultureInfo.InvariantCulture, "a.push([\"{0}\",\"{1}\",\"{2}\"]);", Properties.Resources.StrDefaultLiveSearchMapsUrl, Properties.Resources.StrErrorReadingRssFeed, Properties.Resources.StrCheckRssFeedSettings);
            }

           return scenes.Replace("\n"," ");
        }

        /// <summary>
        /// Mouse Move Event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScreenSaverForm_MouseMove(object sender, MouseEventArgs e)
        {
            // Set IsActive and MouseLocation only the first time this event is called.
            if (!isActive)
            {
                mouseLocation = MousePosition;
                isActive = true;
            }
            else
            {
                // If the mouse has moved significantly since first call, close.
                if ((Math.Abs(MousePosition.X - mouseLocation.X) > 10) ||
                    (Math.Abs(MousePosition.Y - mouseLocation.Y) > 10))
                {
                    ExitApplication(Program.ExitCode.Exit);
                }
            }
        }

        /// <summary>
        /// Read an embedded resource
        /// </summary>
        /// <param name="resourceName"></param>
        /// <returns></returns>
        private static string ReadEmbeddedResource(string resourceName)
        {
            string content = string.Empty;
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            {
                if (stream != null)
                {
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        content = reader.ReadToEnd();
                    }
                }
            }
            return content;
        }

        /// <summary>
        /// Mouse Down Event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScreenSaverForm_MouseDown(object sender, MouseEventArgs e)
        {
            ExitApplication(Program.ExitCode.Exit);
        }

        /// <summary>
        /// Process Keyboard events
        /// </summary>
        /// <param name="keyData"></param>
        /// <returns></returns>
        protected override bool ProcessDialogKey(Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Up:
                case Keys.Left:
                case Keys.PageUp:
                    //Load previous scene
                    foreach (WebBrowser webBrowser in webBrowsers)
                    {
                        webBrowser.Document.InvokeScript("LoadPrevScene");
                    }
                    break;
                case Keys.Down:
                case Keys.Right:
                case Keys.PageDown:
                    //Load next scene
                    foreach (WebBrowser webBrowser in webBrowsers)
                    {
                        webBrowser.Document.InvokeScript("LoadNextScene");
                    }
                    break;
                case Keys.Enter:
                    foreach(WebBrowser webBrowser in webBrowsers)
                    {
                        //Open Live Search Maps Url for each webBrowser instance open in the screen saver.
                        Process webProcess = new Process();
                        webProcess.StartInfo.FileName = webBrowser.Document.GetElementById("URL").InnerHtml.Replace("&amp;", "&");
                        webProcess.StartInfo.UseShellExecute = true;
                        webProcess.Start();
                    }
                    ExitApplication(Program.ExitCode.Exit);
                    break;
                default:
                    //Exit with any other click
                    ExitApplication(Program.ExitCode.Exit);
                    break;
            }
            return true;
        }

        /// <summary>
        /// Feed Update Event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void feedUpdateTimerTick(object sender, EventArgs e)
        {
            ExitApplication(Program.ExitCode.UpdateFeed);
        }

        /// <summary>
        /// Exit the application with an exit code
        /// </summary>
        /// <param name="exitCode"></param>
        private void ExitApplication(Program.ExitCode exitCode)
        {
            foreach (Form form in emptyForms)
            {
                form.Close();
            }
            this.ReturnCode = exitCode;
            Close();
        }
    }
}
